package com.zjc;

import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpReader {

    private static final char SPACE = ' ';
    private static final String BLANK = "";
    private static final String CONTENT_LENGTH = "content-length";
    private static final String NEW_LINE = "\r\n";
    private static final String HEAD_SEPARATOR = ": ";

    /**
     * 原始字符串
     */
    private StringBuilder source = new StringBuilder(512);
    /**
     * 请求头部
     */
    private Map<String, Object> hearders = new HashMap<>(10);
    /**
     * 请求参数
     */
    private StringBuilder body = new StringBuilder();
    /**
     * 请求输入流
     */
    private BufferedReader reader;
    /**
     * 已读取数据长度
     */
    private int readLength = 0;
    /**
     * 请求方式
     */
    private String method;
    /**
     * URI
     */
    private String uri;
    /**
     * 版本
     */
    private String version;
    /**
     * URL中的目标地址
     */
    private Connect connect;
    /**
     * target host
     */
    private String targetHost;


    public HttpReader(InputStream inputStream) {
        this.reader = new BufferedReader(new InputStreamReader(inputStream));
    }

    public HttpReader(Socket socket) throws IOException {
        this(socket.getInputStream());
    }

    private String readLine() {
        try {
            String line = this.reader.readLine();
            if (line == null) {
                return BLANK;
            }
            this.source.append(line).append(NEW_LINE);
            this.readLength += line.getBytes().length + 2;
            return line;
        } catch (IOException e) {
            throw new RuntimeException("HTTP parse error");
        }
    }

    public static final Pattern ISSUED_DATA = Pattern.compile("issuedUri=[^&]+");

    private void parseProtocol() {
        String line = null;
        while (line == null) {
            line = this.readLine();
        }
        int size = line.length(), i = -1, lastBeginIndex = 0, time = 0;
        String value;
        while (++i < size) {
            if (line.charAt(i) == SPACE) {
                value = line.substring(lastBeginIndex, i);
                lastBeginIndex = i + 1;
                if (time == 0) {
                    time++;
                    this.method = value;
                } else if (time == 1) {
                    this.uri = value;
                    this.version = line.substring(lastBeginIndex);
                }
            }
        }
        /* 从请求端读取目标地址 */
        if (this.connect != null) {
            Matcher matcher = ISSUED_DATA.matcher(this.uri);
            if (matcher.find()) {
                try {
                    String sourceUri = matcher.group(0);
                    this.uri = this.uri.replace(sourceUri, BLANK);
                    if (this.uri.charAt(this.uri.length() - 1) == '?') {
                        this.uri = this.uri.substring(0, this.uri.length() - 1);
                    }
                    String ip;
                    int port;
                    String uri = URLDecoder.decode(sourceUri.substring(10), "utf-8");
                    int index;
                    if ((index = uri.indexOf(":")) > -1 && index < uri.length()) {
                        ip = uri.substring(0, index);
                        port = Integer.parseInt(uri.substring(index + 1));
                        this.targetHost = ip + ':' + port;
                    } else {
                        port = 80;
                        ip = uri;
                        this.targetHost = ip;
                    }
                    this.connect.target = new Socket(ip, port);
                    this.source.delete(0, this.source.length()).append(this.method + " " + this.uri + " " + this.version + NEW_LINE);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    private int parseHearders() {
        String line;
        int index;
        while (true) {
            line = this.readLine();
            index = line.indexOf(HEAD_SEPARATOR);
            if (index > -1) {
                String key = line.substring(0, index).toLowerCase(), value = line.substring(index + 2);
                if (key.equals("host")) {
                    int start = this.source.indexOf(value);
                    this.source.replace(start, start + value.length(), this.targetHost);

                }
                this.hearders.put(key, value);
            }
            if (line.length() == 0) {
                break;
            }
        }
        return this.hearders.size();
    }


    /**
     * PC
     *
     * @param
     * @return Result<>
     * <p>
     * * * * * T * I * M * E * * * * *
     * 创建及修改内容
     * @throws
     * @title content-type 长度，为字节长度， 注意
     * @description
     * @params
     * @author KB
     * @createTime 2021/3/15 11:53
     * @editor KB
     * @updateDesc 原著
     * @updateTime 2021/3/15 11:53
     * @since - 2021/3/15 11:53
     */
    public int parseBody() {

        this.parseProtocol();
        this.parseHearders();
        Object l = this.hearders.get(CONTENT_LENGTH);
        int readSize = 0, contentSize = l == null ? 0 : Integer.parseInt(l.toString());
        if (contentSize == 0) {
            return 0;
        }
        char[] c = new char[contentSize % 512 + 1];
        int length;
        try {
            while ((length = this.reader.read(c)) > -1) {
                body.append(c, 0, length);
                source.append(c, 0, length);
                readSize += length;
                //todo 中文字符长度 ！= 字节长度， 因为是char数组，若统计，相对麻烦， 可以使用 new String(c, 0, length).getBytes().length()
                this.readLength += length;
                if (readSize >= contentSize || length < c.length) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return readSize;
    }

    public String getSource() {
        return source.toString();
    }

    public int getBodyByteSize() {
        return Integer.parseInt(this.hearders.get(CONTENT_LENGTH).toString());
    }

    public BufferedReader getReader() {
        return reader;
    }

    public int getReadLength() {
        return readLength;
    }

    public void setConnect(Connect connect) {
        this.connect = connect;
    }

    public void setTargetHost(String targetHost) {
        this.targetHost = targetHost;
    }
}
